#!/usr/bin/python

import re
import sys

PAT = re.compile(r'(\w+)=(\w+)')

def init_trace():
    d = {}
    d['r00_usr'] = []
    d['r01_usr'] = []
    d['r02_usr'] = []
    d['r03_usr'] = []
    d['r04_usr'] = []
    d['r05_usr'] = []
    d['r06_usr'] = []
    d['r07_usr'] = []
    d['r08_usr'] = []
    d['r09_usr'] = []
    d['r10_usr'] = []
    d['r11_usr'] = []
    d['r12_usr'] = []
    d['r13_usr'] = []
    d['r14_usr'] = []
    d['r13_svc'] = []
    d['r14_svc'] = []
    d['r13_abt'] = []
    d['r14_abt'] = []
    d['r13_und'] = []
    d['r14_und'] = []
    d['r13_irq'] = []
    d['r14_irq'] = []
    d['r08_fiq'] = []
    d['r09_fiq'] = []
    d['r10_fiq'] = []
    d['r11_fiq'] = []
    d['r12_fiq'] = []
    d['r13_fiq'] = []
    d['r14_fiq'] = []
    d['cpsr_nzcv'] = []
    d['cpsr_a'] = []
    d['cpsr_i'] = []
    d['cpsr_f'] = []
    d['cpsr_mode'] = []
    d['spsr_fiq'] = []
    d['spsr_irq'] = []
    d['spsr_svc'] = []
    d['spsr_abt'] = []
    d['spsr_und'] = []
    d['mem'] = []
    return d

def usage():
    print('usage: tracecmp <reference-trace> <user-trace>', file=sys.stderr)
    sys.exit(2)

def parse_trace(f):
    d = init_trace()
    line_num = 0
    annot = ''
    for line in f:
        line_num += 1
        if line[0] in '$%':
            match = PAT.match(line, 1)
            reg, val = match.group(1, 2)
            if len(d[reg]) == 0 or d[reg][-1][0] != val:
                d[reg].append((val, line[:-1], line_num, annot))
        elif line[0] == '@':
            annot = line[:-1]
        elif line[0] == '*':
            match = PAT.match(line, 1)
            addr, val = match.group(1, 2)
            s = '{}={}'.format(addr, val)
            d['mem'].append((s, line[:-1], line_num, annot))
    return d

# returns the earliest mismatch or None if no mismatch is found
# mismatch = (key, ref_val, usr_val, ref_line_num, usr_line_num1, annot)
def compare_trace(ref, usr):
    mismatch_list = []
    for key in ref:
        l0 = ref[key]
        l1 = usr[key]
        for i, rec0 in enumerate(l0):
            val0, line0, line_num0, annot0 = rec0
            if i < len(l1):
                val1, line1, line_num1, annot1 = l1[i]
            else:
                val1, line1, line_num1, annot1 = None, "<EOF>", -1, ''
            if val0 != val1:
                mismatch_list.append((val0, val1, line0, line1, line_num0, line_num1, annot0))
                break
        if len(l1) > len(l0):
            val0, line0, line_num0, annot0 = None, "<EOF>", -1, ''
            val1, line1, line_num1, annot1 = l1[len(l0)]
            mismatch_list.append((val0, val1, line0, line1, line_num0, line_num1, annot0))
    if len(mismatch_list) == 0:
        return None
    # find the earliest mismatch
    # TODO special case -1
    earliest = mismatch_list[0]
    for mismatch in mismatch_list:
        if mismatch[4] < earliest[4]:
            earliest = mismatch
    return mismatch

def main():
    if len(sys.argv) != 3:
        usage()
    ref_filepath = sys.argv[1]
    usr_filepath = sys.argv[2]
    with open(ref_filepath, 'r') as f:
        ref_trace = parse_trace(f)
    with open(usr_filepath, 'r') as f:
        usr_trace = parse_trace(f)
    mismatch = compare_trace(ref_trace, usr_trace)
    if mismatch:
        ref_val, usr_val, ref_line, usr_line, ref_line_num, usr_line_num, annot = mismatch
        print(annot)
        print('%6d: %s' % (ref_line_num, ref_line))
        print('%6d: %s' % (usr_line_num, usr_line))
        return 1
    return 0

if __name__ == "__main__":
    exit(main())
